package com.gitee.apanlh.util.io;

import com.gitee.apanlh.exp.IOCloseErrorException;
import com.gitee.apanlh.exp.StreamCopyException;
import com.gitee.apanlh.exp.StreamWriteException;
import com.gitee.apanlh.util.base.Empty;
import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.encode.CharsetCode;
import com.gitee.apanlh.util.encode.StrEncodeUtils;
import com.gitee.apanlh.util.unit.BuffSize;
import com.gitee.apanlh.util.valid.ValidParam;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.nio.charset.Charset;

/**
 * 	IO流工具类
 *  	
 * 	@author Pan
 */
public class IOUtils {
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private IOUtils() {
		//	不允许外部实例
		super();
	}

	/**	
	 * 	获取缓冲文件读取器
	 * 	<br>默认系统字符编码
	 * 	<br>默认8K缓冲区
	 * 	<br>将保持输入流开放
	 * 
	 * 	@author Pan
	 * 	@param 	file			file对象
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(File file) {
		return getReader(file, CharsetCode.getDefaultToString());
	}
	
	/**	
	 * 	获取缓冲文件读取器
	 * 	<br>自定义系统字符编码
	 * 	<br>默认8K缓冲区
	 * 	<br>将保持输入流开放
	 * 
	 * 	@author Pan
	 * 	@param 	file			file对象
	 * 	@param 	charset			字符编码
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(File file, String charset) {
		return getReader(file, charset, BuffSize.SIZE_8K);
	}
	
	/**	
	 * 	获取缓冲文件读取器
	 * 	<br>默认8K缓冲区
	 * 	<br>自定义字符编码
	 * 	<br>将保持输入流开放
	 * 	
	 * 	@author Pan
	 * 	@param 	file			file对象
	 * 	@param 	charset			字符编码
	 * 	@param 	bufferSize		缓冲区大小
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(File file, String charset, int bufferSize) {
		return getReader(file, CharsetCode.getCharset(charset, CharsetCode.CHARSET_UTF_8), bufferSize);
	}
	
	/**	
	 * 	获取BufferedReader
	 * 	<br>自定义缓冲区大小
	 * 	<br>自定义字符编码
	 * 	<br>将保持输入流开放
	 * 	
	 * 	@author Pan
	 * 	@param 	file			file对象
	 * 	@param 	charset			字符编码集
	 * 	@param 	bufferSize		缓冲区大小
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(File file, Charset charset, int bufferSize) {
		if (file == null) {
			return null;
		}
		return getReader(FileIOUtils.getInput(file), charset, bufferSize);
	}
	
	/**	
	 * 	获取缓冲文件读取器
	 * 	<br>默认系统字符编码
	 * 	<br>默认8K缓冲区
	 * 	<br>将保持输入流开放
	 * 
	 * 	@author Pan
	 * 	@param 	is		输入流
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(InputStream is) {
		return getReader(is, CharsetCode.getDefaultToString());
	}
	
	/**	
	 * 	获取缓冲文件读取器
	 * 	<br>自定义系统字符编码
	 * 	<br>默认8K缓冲区
	 * 	<br>将保持输入流开放
	 * 
	 * 	@author Pan
	 * 	@param 	is		输入流
	 * 	@param 	charset	字符编码
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(InputStream is, String charset) {
		return getReader(is, charset, BuffSize.SIZE_8K);
	}
	
	/**	
	 * 	获取缓冲文件读取器
	 * 	<br>默认8K缓冲区
	 * 	<br>自定义字符编码
	 * 	<br>将保持输入流开放
	 * 	
	 * 	@author Pan
	 * 	@param 	is			输入流
	 * 	@param 	charset		字符编码
	 * 	@param 	bufferSize	缓冲区大小
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(InputStream is, String charset, int bufferSize) {
		return getReader(is, CharsetCode.getCharset(charset, CharsetCode.CHARSET_UTF_8), bufferSize);
	}
	
	/**	
	 * 	获取缓冲文件读取器
	 * 	<br>自定义系统字符编码
	 * 	<br>默认8K缓冲区
	 * 	<br>将保持输入流开放
	 * 
	 * 	@author Pan
	 * 	@param 	is		输入流
	 * 	@param 	charset	字符编码
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(InputStream is, Charset charset) {
		return getReader(is, charset, BuffSize.SIZE_8K);
	}
	
	/**	
	 * 	获取BufferedReader
	 * 	<br>自定义缓冲区大小
	 * 	<br>自定义字符编码
	 * 	<br>将保持输入流开放
	 * 	
	 * 	@author Pan
	 * 	@param 	is				输入流
	 * 	@param 	charset			字符编码集
	 * 	@param 	bufferSize		缓冲区大小
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReader(InputStream is, Charset charset, int bufferSize) {
		if (is == null) {
			return null;
		}
		
		if (bufferSize <= 0) {
			bufferSize = BuffSize.SIZE_8K;
		}
		
		return new BufferedReader(new InputStreamReader(is, charset), bufferSize);
	}
	
	/**	
	 * 	获取BufferedReader
	 * 	<br>指定UTF-8字符编码
	 * 	
	 * 	@author Pan
	 * 	@param 	is		输入流
	 * 	@return	BufferedReader
	 */
	public static BufferedReader getReaderUtf8(InputStream is) {
		return getReader(is, CharsetCode.CHARSET_UTF_8, BuffSize.SIZE_8K);
	}
	
	/**
	 * 	byte[]转换输入流ByteArrayStream
	 * 	
	 * 	@author Pan
	 * 	@param  bytes	字节数组
	 * 	@return ByteArrayStream
	 */
	public static ByteArrayInputStream toByteInput(byte[] bytes) {
		return new ByteArrayInputStream(bytes);
	}
	
	/**
	 * 	字节转换成字节缓冲输出流(ByteArrayOutputStream)
	 * 	<br>默认8K缓冲大小
	 * 	
	 * 	@author Pan
	 * 	@param  bytes		字节数组
	 * 	@return ByteArrayOutputStream
	 */
	public static ByteArrayOutputStream toByteOutput(byte[] bytes) {
	   return toByteOutput(bytes, BuffSize.SIZE_8K);
	}
	
	/**
	 * 	字节转换成字节缓冲输出流(ByteArrayOutputStream)
	 * 	<br>自定义缓冲大小
	 * 	
	 * 	@author Pan
	 * 	@param  bytes		字节数组
	 * 	@param 	bufferSize	缓冲大小
	 * 	@return ByteArrayOutputStream
	 */
	public static ByteArrayOutputStream toByteOutput(byte[] bytes, int bufferSize) {
	    ByteArrayOutputStream baos = new ByteArrayOutputStream(bufferSize);
        write(bytes, baos, bufferSize);
	    return baos;
	}

	/**	
	 * 	将输入流中内容复制至输出流
	 * 	<br>默认8K缓冲区大小
	 * 	<br>默认自动关闭输入流
	 * 	<br>默认不会自动关闭输出流
	 * 	<br>出现异常时将自动关闭输出/输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is				输入流
	 * 	@param 	os				输出流
	 */
	public static void copy(InputStream is, OutputStream os) {
		copy(is, os, BuffSize.SIZE_8K);
	}
	
	/**	
	 * 	将输入流中内容复制至输出流
	 *  <br>默认8K缓冲区长度
	 * 	<br>自定义关闭输入/输出流(两者全关)
	 * 	<br>出现异常时将自动关闭输出/输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is					输入流
	 * 	@param 	os					输出流
	 * 	@param 	isCloseAll			true关闭输入流及输出流
	 */
	public static void copy(InputStream is, OutputStream os, boolean isCloseAll) {
		copy(is, os, isCloseAll, isCloseAll);
	}

	/**	
	 * 	将输入流中内容复制至输出流
	 * 	<br>默认8K缓冲区长度
	 * 	<br>自定义关闭输出流
	 * 	<br>自定义关闭输入流
	 * 	<br>出现异常时将自动关闭输出/输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is					输入流
	 * 	@param 	os					输出流
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@param 	isCloseOutputStream	true关闭输出流
	 */
	public static void copy(InputStream is, OutputStream os, boolean isCloseInputStream, boolean isCloseOutputStream) {
		copy(is, os, BuffSize.SIZE_8K, isCloseInputStream, isCloseOutputStream);
	}
	
	/**	
	 * 	将输入流中内容复制至输出流
	 * 	<br>自定义缓冲区大小
	 * 	<br>自动关闭输入流
	 * 	<br>不会自动关闭输出流
	 * 	<br>出现异常时将自动关闭输出/输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is				输入流
	 * 	@param 	os				输出流
	 * 	@param 	bufferSize		缓冲区大小
	 */
	public static void copy(InputStream is, OutputStream os, int bufferSize) {
		copy(is, os, bufferSize, true, false);
	}

	/**	
	 * 	将输入流中内容复制至输出流
	 * 	<br>自定义缓冲区长度
	 * 	<br>自定义关闭输入/输出流(两者全关)
	 * 	<br>出现异常时将自动关闭输出/输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is					输入流
	 * 	@param 	os					输出流
	 * 	@param 	bufferSize			缓冲区大小
	 * 	@param 	isCloseAll			true关闭输入流及输出流
	 */
	public static void copy(InputStream is, OutputStream os, int bufferSize, boolean isCloseAll) {
		copy(is, os, bufferSize, isCloseAll, isCloseAll);
	}

	/**	
	 * 	将输入流中内容复制至输出流
	 * 	<br>自定义缓冲区长度
	 * 	<br>自定义关闭输出流
	 * 	<br>自定义关闭输入流
	 * 	<br>出现异常时将自动关闭输出/输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is					输入流
	 * 	@param 	os					输出流
	 * 	@param 	bufferSize			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@param 	isCloseOutputStream	true关闭输出流
	 */
	public static void copy(InputStream is, OutputStream os, int bufferSize, boolean isCloseInputStream, boolean isCloseOutputStream) {
		boolean isEx = false;
		
		try {
			byte[] buffer = BuffSize.newBufferByte(bufferSize); 
			int len;
			while ((len = is.read(buffer)) != -1) {
				os.write(buffer, 0, len);
				os.flush();
			}
		} catch (Exception e) {
			isEx = true;
			close(os, is);	
			throw new StreamCopyException(e.getMessage(), e);
		} finally {
			if (!isEx) {
				if (isCloseOutputStream) {
					close(os);
				}
				if (isCloseInputStream) {
					close(is);
				}
			}
		}
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到StringBuilder中
	 *	<br>默认8K缓冲长度
	 * 	<br>自动关闭字符输入流
	 * 	<br>异常时自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	reader     字符输入流
	 * 	@param 	out        要写入的StringBuilder对象
	 */
	public static void write(Reader reader, StringBuilder out) {
		write(reader, out, true);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到StringBuilder中
	 *	<br>默认8K缓冲长度
	 *	<br>自定义关闭字符输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     			字符输入流
	 * 	@param 	out        			要写入的StringBuilder对象
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(Reader reader, StringBuilder out, boolean isCloseInputStream) {
		write(reader, out, BuffSize.SIZE_8K, isCloseInputStream);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到StringBuilder中
	 *	<br>自动关闭字符输入流
	 *	<br>自定义缓冲长度
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     字符输入流
	 * 	@param 	out        要写入的StringBuilder对象
	 * 	@param 	bufferSize 缓冲区大小
	 */
	public static void write(Reader reader, StringBuilder out, int bufferSize) {
		write(reader, out, bufferSize, true);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到StringBuilder中
	 *	<br>自定义缓冲长度
	 *	<br>自定义关闭字符输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     			字符输入流
	 * 	@param 	out        			要写入的StringBuilder对象
	 * 	@param 	bufferSize 			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(Reader reader, StringBuilder out, int bufferSize, boolean isCloseInputStream) {
		write(reader, (Appendable) out, bufferSize, isCloseInputStream);
	}

	/**
	 * 	将字符输入流中的字符内容写入到StringBuffer中
	 *	<br>默认8K缓冲长度
	 * 	<br>自动关闭字符输入流
	 * 	<br>异常时自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	reader     字符输入流
	 * 	@param 	out        要写入的StringBuffer对象
	 */
	public static void write(Reader reader, StringBuffer out) {
		write(reader, out, true);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到StringBuffer中
	 *	<br>默认8K缓冲长度
	 *	<br>自定义关闭字符输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     			字符输入流
	 * 	@param 	out        			要写入的StringBuffer对象
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(Reader reader, StringBuffer out, boolean isCloseInputStream) {
		write(reader, out, BuffSize.SIZE_8K, isCloseInputStream);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到StringBuffer中
	 *	<br>自动关闭字符输入流
	 *	<br>自定义缓冲长度
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     字符输入流
	 * 	@param 	out        要写入的StringBuffer对象
	 * 	@param 	bufferSize 缓冲区大小
	 */
	public static void write(Reader reader, StringBuffer out, int bufferSize) {
		write(reader, out, bufferSize, true);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到StringBuffer中
	 *	<br>自定义缓冲长度
	 *	<br>自定义关闭字符输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     			字符输入流
	 * 	@param 	out        			要写入的StringBuffer对象
	 * 	@param 	bufferSize 			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(Reader reader, StringBuffer out, int bufferSize, boolean isCloseInputStream) {
		write(reader, (Appendable) out, bufferSize, isCloseInputStream);
	}

	/**
	 * 	将字符输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>默认8K缓冲长度
	 * 	<br>自动关闭字符输入流
	 * 	<br>异常时自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	reader     字符输入流
	 * 	@param 	out        要写入的Appendable对象
	 */
	public static void write(Reader reader, Appendable out) {
		write(reader, out, true);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>默认8K缓冲长度
	 *	<br>自定义关闭字符输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     			字符输入流
	 * 	@param 	out        			要写入的Appendable对象
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(Reader reader, Appendable out, boolean isCloseInputStream) {
		write(reader, out, BuffSize.SIZE_8K, isCloseInputStream);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>自动关闭字符输入流
	 *	<br>自定义缓冲长度
	 *	<br>异常时自动关闭输入流
	 *
	 * 	@author Pan
	 * 	@param 	reader     字符输入流
	 * 	@param 	out        要写入的Appendable对象
	 * 	@param 	bufferSize 缓冲区大小
	 */
	public static void write(Reader reader, Appendable out, int bufferSize) {
		write(reader, out, bufferSize, true);
	}
	
	/**
	 * 	将字符输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>自定义缓冲长度
	 *	<br>自定义关闭字符输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 * 	@author Pan
	 * 	@param 	reader     			字符输入流
	 * 	@param 	out        			要写入的Appendable对象
	 * 	@param 	bufferSize 			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(Reader reader, Appendable out, int bufferSize, boolean isCloseInputStream) {
		boolean isEx = false;
		try {
			char[] buffer = BuffSize.newBufferChar(bufferSize);
			int len;
			
			//	根据 Appendable不用类型执行操作
			if (out instanceof StringBuilder) {
				StringBuilder stringBuilder = ((StringBuilder) out);
				while ((len = reader.read(buffer)) != -1) {
					stringBuilder.append(buffer, 0, len);
				}
				return ;
			} 
			if (out instanceof StringBuffer) {
				StringBuffer stringBuffer = ((StringBuffer) out);
				while ((len = reader.read(buffer)) != -1) {
					stringBuffer.append(buffer, 0, len);
				}
				return ;
			}
			
			//	其他Appendable接口类型
			while ((len = reader.read(buffer)) != -1) {
				appendableAppend(out, buffer, 0, len);
			}
		} catch (Exception e) {
			isEx = true;
			close(reader);
			throw new StreamWriteException(e.getMessage() ,e);
		} finally {
			if (!isEx && isCloseInputStream) {
				close(reader);
			}
		}
	}

	/**
	 *	将输入流中的字符内容写入到StringBuilder中
	 *	<br>默认8K缓冲区大小
	 *	<br>默认系统字符编码
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的StringBuilder对象
	 */
	public static void write(InputStream is, StringBuilder out) {
		write(is, out, true);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuilder中
	 *	<br>默认8K缓冲区大小
	 *	<br>默认系统字符编码
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的StringBuffer对象
	 * 	@param 	isCloseOutputStream	true关闭输入流
	 */
	public static void write(InputStream is, StringBuilder out, boolean isCloseOutputStream) {
		write(is, out, CharsetCode.getDefaultToString(), isCloseOutputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuilder中
	 *	<br>默认8K缓冲区大小
	 *	<br>自定义字符编码
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的StringBuffer对象
	 * 	@param 	charset    字符编码
	 */
	public static void write(InputStream is, StringBuilder out, String charset) {
		write(is, out, charset, BuffSize.SIZE_8K);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuilder中
	 *	<br>默认8K缓冲区大小
	 *	<br>自定义字符编码
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的StringBuilder对象
	 * 	@param 	charset    			字符编码
	 * 	@param 	isCloseOutputStream	true关闭输出流
	 */
	public static void write(InputStream is, StringBuilder out, String charset, boolean isCloseOutputStream) {
		write(is, out, charset, BuffSize.SIZE_8K, isCloseOutputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuilder中
	 *	<br>自定义字符编码
	 *	<br>自定义缓冲区长度
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的StringBuilder对象
	 * 	@param 	charset    字符编码
	 * 	@param 	bufferSize 缓冲区大小
	 */
	public static void write(InputStream is, StringBuilder out, String charset, int bufferSize) {
		write(is, out, charset, bufferSize, true);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuilder中
	 *	<br>自定义字符编码
	 *	<br>自定义缓冲区长度
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的StringBuilder对象
	 * 	@param 	charset    			字符编码
	 * 	@param 	bufferSize 			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(InputStream is, StringBuilder out, String charset, int bufferSize, boolean isCloseInputStream) {
		write(is, (Appendable) out, charset, bufferSize, isCloseInputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuffer中
	 *	<br>默认8K缓冲区大小
	 *	<br>默认系统字符编码
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的StringBuffer对象
	 */
	public static void write(InputStream is, StringBuffer out) {
		write(is, out, true);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuffer中
	 *	<br>默认8K缓冲区大小
	 *	<br>默认系统字符编码
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的StringBuffer对象
	 * 	@param 	isCloseOutputStream	true关闭输入流
	 */
	public static void write(InputStream is, StringBuffer out, boolean isCloseOutputStream) {
		write(is, out, CharsetCode.getDefaultToString(), isCloseOutputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuffer中
	 *	<br>默认8K缓冲区大小
	 *	<br>自定义字符编码
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的StringBuffer对象
	 * 	@param 	charset    字符编码
	 */
	public static void write(InputStream is, StringBuffer out, String charset) {
		write(is, out, charset, BuffSize.SIZE_8K);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuffer中
	 *	<br>默认8K缓冲区大小
	 *	<br>自定义字符编码
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的StringBuffer对象
	 * 	@param 	charset    			字符编码
	 * 	@param 	isCloseOutputStream	true关闭输出流
	 */
	public static void write(InputStream is, StringBuffer out, String charset, boolean isCloseOutputStream) {
		write(is, out, charset, BuffSize.SIZE_8K, isCloseOutputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuffer中
	 *	<br>自定义字符编码
	 *	<br>自定义缓冲区长度
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的StringBuffer对象
	 * 	@param 	charset    字符编码
	 * 	@param 	bufferSize 缓冲区大小
	 */
	public static void write(InputStream is, StringBuffer out, String charset, int bufferSize) {
		write(is, out, charset, bufferSize, true);
	}
	
	/**
	 *	将输入流中的字符内容写入到StringBuffer中
	 *	<br>自定义字符编码
	 *	<br>自定义缓冲区长度
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的StringBuffer对象
	 * 	@param 	charset    			字符编码
	 * 	@param 	bufferSize 			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(InputStream is, StringBuffer out, String charset, int bufferSize, boolean isCloseInputStream) {
		write(is, (Appendable) out, charset, bufferSize, isCloseInputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>默认8K缓冲区大小
	 *	<br>默认系统字符编码
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的Appendable对象
	 */
	public static void write(InputStream is, Appendable out) {
		write(is, out, true);
	}
	
	/**
	 *	将输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>默认8K缓冲区大小
	 *	<br>默认系统字符编码
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的Appendable对象
	 * 	@param 	isCloseOutputStream	true关闭输入流
	 */
	public static void write(InputStream is, Appendable out, boolean isCloseOutputStream) {
		write(is, out, CharsetCode.getDefaultToString(), isCloseOutputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>默认8K缓冲区大小
	 *	<br>自定义字符编码
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的Appendable对象
	 * 	@param 	charset    字符编码
	 */
	public static void write(InputStream is, Appendable out, String charset) {
		write(is, out, charset, BuffSize.SIZE_8K);
	}
	
	/**
	 *	将输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>默认8K缓冲区大小
	 *	<br>自定义字符编码
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的Appendable对象
	 * 	@param 	charset    			字符编码
	 * 	@param 	isCloseOutputStream	true关闭输出流
	 */
	public static void write(InputStream is, Appendable out, String charset, boolean isCloseOutputStream) {
		write(is, out, charset, BuffSize.SIZE_8K, isCloseOutputStream);
	}
	
	/**
	 *	将输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>自定义字符编码
	 *	<br>自定义缓冲区长度
	 *	<br>自动关闭输入流
	 *	<br>异常时自动关闭输入流
	 *
	 *	@author Pan
	 * 	@param 	is         输入流
	 * 	@param 	out        要写入的Appendable对象
	 * 	@param 	charset    字符编码
	 * 	@param 	bufferSize 缓冲区大小
	 */
	public static void write(InputStream is, Appendable out, String charset, int bufferSize) {
		write(is, out, charset, bufferSize, true);
	}
	
	/**
	 *	将输入流中的字符内容写入到Appendable中(适配StringBuilder及StringBuffer)
	 *	<br>自定义字符编码
	 *	<br>自定义缓冲区长度
	 *	<br>自定义关闭输入流
	 *	<br>异常时自动关闭输入流
	 *	
	 *	@author Pan
	 * 	@param 	is         			输入流
	 * 	@param 	out        			要写入的Appendable对象
	 * 	@param 	charset    			字符编码
	 * 	@param 	bufferSize 			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 */
	public static void write(InputStream is, Appendable out, String charset, int bufferSize, boolean isCloseInputStream) {
		if (is == null) {
			return;
		}
		if (ValidParam.isEmpty(charset)) {
			charset = CharsetCode.getDefaultToString();
		}
		
		boolean isEx = false;
		BufferedReader reader = null;
		try {
			reader = getReader(is, charset);
			
			char[] buffer = BuffSize.newBufferChar(bufferSize);
			int len;
			
			//	根据 Appendable不用类型执行操作
			if (out instanceof StringBuilder) {
				StringBuilder stringBuilder = ((StringBuilder) out);
				while ((len = reader.read(buffer)) != -1) {
					stringBuilder.append(buffer, 0, len);
				}
				return ;
			} 
			if (out instanceof StringBuffer) {
				StringBuffer stringBuffer = ((StringBuffer) out);
				while ((len = reader.read(buffer)) != -1) {
					stringBuffer.append(buffer, 0, len);
				}
				return ;
			}
			
			//	其他Appendable接口类型
			while ((len = reader.read(buffer)) != -1) {
				appendableAppend(out, buffer, 0, len);
			}
		} catch (Exception e) {
			isEx = true;
			IOUtils.close(reader);
			throw new StreamWriteException(e.getMessage(), e);
		} finally {
			if (!isEx && isCloseInputStream) {
				IOUtils.close(reader);
			}
		}
	}
	
	/**
	 * 	将字符串写入输出流
	 * 	<br>默认8K缓冲区大小
	 * 	<br>默认系统字符编码
	 * 	<br>默认自动关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 	
	 * 	@author Pan
	 * 	@param 	str		字符串
	 * 	@param 	out		输出流
	 */
	public static void write(String str, OutputStream out) {
		write(str, out, true);
	}
	
	/**
	 * 	将字符串写入输出流
	 * 	<br>默认8K缓冲区大小
	 * 	<br>默认系统字符编码
	 * 	<br>自定义选择关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 	
	 * 	@author Pan
	 * 	@param 	str					字符串
	 * 	@param 	out					输出流
	 * 	@param 	closeOutputStream	true关闭输出流
	 */
	public static void write(String str, OutputStream out, boolean closeOutputStream) {
		write(str, out, CharsetCode.getDefaultToString(), BuffSize.SIZE_8K, closeOutputStream);
	}
	
	/**
	 * 	将字符串写入输出流
	 * 	<br>自定义缓冲区大小
	 * 	<br>默认系统字符编码
	 * 	<br>默认自动关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 	
	 * 	@author Pan
	 * 	@param 	str					字符串
	 * 	@param 	out					输出流
	 * 	@param 	bufferSize			缓冲区长度
	 */
	public static void write(String str, OutputStream out, int bufferSize) {
		write(str, out, CharsetCode.getDefaultToString(), bufferSize, true);
	}
	
	/**
	 * 	将字符串写入输出流
	 * 	<br>默认8K缓冲区大小
	 * 	<br>自定义字符编码
	 * 	<br>默认自动关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 	
	 * 	@author Pan
	 * 	@param 	str		字符串
	 * 	@param 	out		输出流
	 * 	@param 	charset	字符编码
	 */
	public static void write(String str, OutputStream out, String charset) {
		write(str, out, charset, BuffSize.SIZE_8K);
	}
	
	/**
	 * 	将字符串写入输出流
	 * 	<br>默认8K缓冲区大小
	 * 	<br>自定义字符编码
	 * 	<br>自定义选择关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 	
	 * 	@author Pan
	 * 	@param 	str					字符串
	 * 	@param 	out					输出流
	 * 	@param 	charset				字符编码
	 * 	@param 	closeOutputStream	true关闭输出流
	 */
	public static void write(String str, OutputStream out, String charset, boolean closeOutputStream) {
		write(str, out, charset, BuffSize.SIZE_8K, closeOutputStream);
	}
	
	/**
	 * 	将字符串写入输出流
	 * 	<br>自定义缓冲区大小
	 * 	<br>自定义字符编码
	 * 	<br>默认自动关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 	
	 * 	@author Pan
	 * 	@param 	str					字符串
	 * 	@param 	out					输出流
	 * 	@param 	charset				字符编码
	 * 	@param 	bufferSize			缓冲区长度
	 */
	public static void write(String str, OutputStream out, String charset, int bufferSize) {
		write(str, out, charset, bufferSize, true);
	}
	
	/**
	 * 	将字符串写入输出流
	 * 	<br>自定义缓冲区大小
	 * 	<br>自定义字符编码
	 * 	<br>自定义选择关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 	
	 * 	@author Pan
	 * 	@param 	str					字符串
	 * 	@param 	out					输出流
	 * 	@param 	charset				字符编码
	 * 	@param 	bufferSize			缓冲区长度
	 * 	@param 	closeOutputStream	true关闭输出流
	 */
	public static void write(String str, OutputStream out, String charset, int bufferSize, boolean closeOutputStream) {
		if (ValidParam.isEmpty(charset)) {
			charset = CharsetCode.getDefaultToString();
		}
		if (Eq.str(charset, CharsetCode.getDefaultToString())) {
			write(StrEncodeUtils.utf8EncodeToBytes(str, CharsetCode.getDefaultToCharset()), out, bufferSize, closeOutputStream);
			return ;
		}
		write(StrEncodeUtils.utf8EncodeToBytes(str, charset), out, bufferSize, closeOutputStream);
	}
	
	/**
	 * 	将byte[]写入输出流
	 * 	<br>默认8K缓冲区大小
	 * 	<br>自动关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 
	 * 	@author Pan
	 * 	@param 	bytes	字节
	 * 	@param 	out		输出流
	 */
	public static void write(byte[] bytes, OutputStream out) {
		write(bytes, out, true);
	}
	
	/**
	 * 	将byte[]写入输出流
	 * 	<br>默认8K缓冲区大小
	 * 	<br>自定义选择关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 
	 * 	@author Pan
	 * 	@param 	bytes				字节
	 * 	@param 	out					输出流
	 * 	@param 	isCloseOutputStream	true关闭输出流
	 */
	public static void write(byte[] bytes, OutputStream out, boolean isCloseOutputStream) {
		write(bytes, out, BuffSize.SIZE_8K, isCloseOutputStream);
	}
	
	/**
	 * 	将byte[]写入输出流
	 * 	<br>自定义缓冲区大小
	 * 	<br>自动关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 
	 * 	@author Pan
	 * 	@param 	bytes		字节
	 * 	@param 	out			输出流
	 * 	@param 	bufferSize	缓冲长度
	 */
	public static void write(byte[] bytes, OutputStream out, int bufferSize) {
		write(bytes, out, bufferSize, true);
	}
	
	/**
	 * 	将byte[]写入输出流
	 * 	<br>自定义缓冲区大小
	 * 	<br>自定义选择关闭输出流
	 * 	<br>异常时将自动关闭输出流
	 * 
	 * 	@author Pan
	 * 	@param 	bytes				字节
	 * 	@param 	out					输出流
	 * 	@param 	bufferSize			缓冲长度
	 * 	@param 	isCloseOutputStream	true关闭输出流
	 */
	public static void write(byte[] bytes, OutputStream out, int bufferSize, boolean isCloseOutputStream) {
		if (out instanceof ByteArrayOutputStream) {
			ByteArrayOutputStream baos = (ByteArrayOutputStream) out;
			try {
				baos.write(bytes);
			} catch (Exception e) {
				close(out);
				throw new StreamWriteException(e.getMessage(), e);
			}
			return ;
		}
		
		copy(toByteInput(bytes), out, bufferSize, true, isCloseOutputStream);
	}
	
	/**	
	 * 	读取输入流
	 * 	<br>自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is		输入流
	 * 	@return	byte[]
	 */
	public static byte[] read(InputStream is) {
		return read(is, true);
	}
	
	/**	
	 * 	读取输入流
	 * 	<br>自定义关闭输入流
	 * 	<br>异常时自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is					输入流
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@return	byte[]
	 */
	public static byte[] read(InputStream is, boolean isCloseInputStream) {
		if (is == null) {
			return Empty.arrayByte();
		}
		
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		copy(is, baos, BuffSize.SIZE_8K, isCloseInputStream, true);
		return baos.toByteArray();
	}
	
	/**
	 * 	读取输入流，返回字符串
	 * 	<br>默认8K缓冲区大小
	 * 	<br>默认系统字符编码
	 * 	<br>自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param  is	输入流
	 * 	@return String
	 */
	public static String readString(InputStream is) {
		return readString(is, CharsetCode.getDefaultToString());
	}
	
	/**
	 * 	读取输入流，返回字符串
	 * 	<br>默认系统字符编码
	 * 	<br>自动关闭输入流
	 * 	<br>自定义缓冲区长度
	 * 	
	 * 	@author Pan
	 * 	@param 	is 					输入流
	 * 	@param 	bufferSize			缓冲区大小
	 * 	@return	String
	 */
	public static String readString(InputStream is, int bufferSize) {
		return readString(is, bufferSize, true);
	}
	
	/**
	 * 	读取输入流，返回字符串
	 * 	<br>默认系统字符编码
	 * 	<br>自定义缓冲区大小
	 * 	<br>自定义关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is 					输入流
	 * 	@param 	bufferSize			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@return	String
	 */
	public static String readString(InputStream is, int bufferSize, boolean isCloseInputStream) {
		return readString(is, CharsetCode.getDefaultToString(), bufferSize, isCloseInputStream);
	}
	
	/**
	 * 	读取输入流，返回字符串
	 * 	<br>默认8K缓冲区大小
	 * 	<br>默认系统字符编码
	 * 	<br>自定义关闭输入流
	 * 	<br>异常时自动关闭输入流
	 * 
	 * 	@author Pan
	 * 	@param  is	输入流
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@return String
	 */
	public static String readString(InputStream is, boolean isCloseInputStream) {
		return readString(is, CharsetCode.getDefaultToString(), isCloseInputStream);
	}
	
	/**
	 * 	读取输入流，返回字符串
	 * 	<br>默认8K缓冲区大小
	 * 	<br>自定义字符编码
	 * 	<br>自动关闭输入流
	 * 	<br>异常时自动关闭输入流
	 * 
	 * 	@author Pan
	 * 	@param 	is 				输入流
	 * 	@param 	charset			编码
	 * 	@return	String
	 */
	public static String readString(InputStream is, String charset) {
		return readString(is, charset, BuffSize.SIZE_8K, true);
	}
	
	/**
	 * 	读取输入流，返回字符串
	 * 	<br>默认8K缓冲区大小
	 * 	<br>自定义关闭输入流
	 * 	<br>自定义字符编码
	 * 	
	 * 	@author Pan
	 * 	@param 	is 					输入流
	 * 	@param 	charset				编码
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@return	String
	 */
	public static String readString(InputStream is, String charset, boolean isCloseInputStream) {
		return readString(is, charset, BuffSize.SIZE_8K, isCloseInputStream);
	}
	
	/**
	 * 	读取输入流，返回字符串
	 * 	<br>自定义字符编码
	 * 	<br>自定义缓冲区大小
	 * 	<br>自定义关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param 	is 					输入流
	 * 	@param 	charset				编码
	 * 	@param 	bufferSize			缓冲区大小
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@return	String
	 */
	public static String readString(InputStream is, String charset, int bufferSize, boolean isCloseInputStream) {
		if (is == null) {
			return null;
		}
		
		StringBuilder out = new StringBuilder();
		write(is, out, charset, bufferSize, isCloseInputStream);
		return out.toString();
	}
	
	/**	
	 *	读取字符输入流将转换String
	 * 	<br>将关闭字符输入流
	 * 	<br>异常时自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param  reader	字符输入流
	 * 	@return	String
	 */
	public static String readString(Reader reader) {
		return readString(reader, true);
	}
	
	/**	
	 *	读取字符输入流将转换String
	 * 	<br>自定义关闭字符输入流
	 * 	<br>异常时自动关闭输入流
	 * 	
	 * 	@author Pan
	 * 	@param  reader	字符输入流
	 * 	@param 	isCloseInputStream	true关闭输入流
	 * 	@return	String
	 */
	public static String readString(Reader reader, boolean isCloseInputStream) {
		if (reader == null) {
			return null;
		}
		
		BufferedReader buffReader;
		if (!(reader instanceof BufferedReader)) {
			buffReader = new BufferedReader(reader);
		} else {
			buffReader = (BufferedReader) reader;
		}
		
		StringBuilder out = new StringBuilder();
		write(buffReader, out, isCloseInputStream);
		return out.toString();
	}
	
	/**	
	 * 	关闭流方法
	 * 	<br>在finally中使用此方法
	 * 
	 * 	@author Pan
	 * 	@param  closeable 关闭接口
	 */
	public static void close(final Closeable closeable) {
		if (null != closeable) {
			try {
				closeable.close();
			} catch (final IOException e) {
				throw new IOCloseErrorException(StringUtils.format("close关闭流方法错误:{}", e.getMessage()), e);
			}
		}
	}
	
	/**	
	 * 	关闭多个流方法
	 * 	<br>注意关闭顺序
	 * 	<br>在finally中使用此方法
	 * 
	 * 	@author Pan
	 * 	@param  closeables 关闭接口
	 */
	public static void close(final Closeable... closeables) {
		if (ValidParam.isNotEmpty(closeables)) {
			for (int i = 0, len = closeables.length; i < len; i++) {
				close(closeables[i]);
			}
		}
	}

	/**	
	 * 	关闭流方法
	 * 	<br>在finally中使用此方法
	 * 
	 * 	@author Pan
	 * 	@param  closeable 关闭接口
	 */
	public static void close(AutoCloseable closeable) {
		try {
			if (ValidParam.isNotNull(closeable)) {
				closeable.close();
			}
		} catch (final Exception e) {
			throw new IOCloseErrorException(StringUtils.format("close关闭流方法错误:{}", e.getMessage()), e);
		}
	}
	
	/**	
	 * 	关闭多个流方法
	 * 	<br>注意关闭顺序
	 * 	<br>在finally中使用此方法
	 * 
	 * 	@author Pan
	 * 	@param  closeables 关闭接口
	 */
	public static void close(AutoCloseable... closeables) {
		if (ValidParam.isNotEmpty(closeables)) {
			for (int i = 0, len = closeables.length; i < len; i++) {
				close(closeables[i]);
			}
		}
	}
	
	/**	
	 * 	关闭方法
	 * 	<br>在finally中使用此方法
	 * 
	 * 	@author Pan
	 * 	@param  process 关闭接口
	 */
	public static void close(Process process) {
		try {
			if (ValidParam.isNotNull(process)) {
				process.destroy();
			}
		} catch (final Exception e) {
			throw new IOCloseErrorException(StringUtils.format("Process关闭流方法错误:{}", e.getMessage()), e);
		}
	}
	
	/**	
	 * 	关闭方法
	 * 	<br>在finally中使用此方法
	 * 
	 * 	@author Pan
	 * 	@param  connection	HTTP客户端
	 */
	public static void close(HttpURLConnection connection) {
		try {
			if (ValidParam.isNotNull(connection)) {
				connection.disconnect();
			}
		} catch (final Exception e) {
			throw new IOCloseErrorException(StringUtils.format("HttpURLConnection关闭流方法错误:{}", e.getMessage()), e);
		}
	}
	
	/**
	 * 	将字符数组追加到Appendable中
	 *	
	 *	@author Pan
	 * 	@param 	appendable  Appendable接口
	 * 	@param 	buffer 		缓冲数组
	 * 	@param 	start  		开始位置
	 * 	@param 	length 		追加长度
	 * 	@throws IOException I/O错误
	 */
	private static void appendableAppend(Appendable appendable, char[] buffer, int start, int length) throws IOException {
	    for (int i = start; i < start + length; i++) {
	    	appendable.append(buffer[i]);
	    }
	}
}
